home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / comm / wnos5src.zip / POPSERV.C < prev    next >
Text File  |  1993-08-09  |  10KB  |  444 lines

  1. /* POP Server state machine - see RFC 937
  2.  *
  3.  *  also see other credits in popcli.c
  4.  *  10/89 Mike Stockett wa7dyx
  5.  *  Modified 5/27/90 by Allen Gwinn, N5CKP, for later NOS releases.
  6.  *  Added to NOS by PA0GRI 2/6/90 (and linted into "standard" C)
  7.  *    some code streaming - DB3FL.911111
  8.  */
  9.  
  10. #include <stdio.h>
  11. #include <time.h>
  12. #include <sys/stat.h>
  13. #if    defined(__STDC__) || defined(__TURBOC__)
  14. #include <stdarg.h>
  15. #endif
  16. #include <ctype.h>
  17. #include <setjmp.h>
  18. #include "global.h"
  19. #include "config.h"
  20. #ifdef POP
  21. #include "mbuf.h"
  22. #include "cmdparse.h"
  23. #include "socket.h"
  24. #include "proc.h"
  25. #include "files.h"
  26. #include "pop.h"
  27. #include "server.h"
  28.  
  29. #define    BITS_PER_WORD        16
  30. #define isSOM(x)        ((strncmp(x,"From ",5) == 0))
  31.  
  32. /* Command string specifications */
  33. static char    ackd_cmd[]     = "ACKD",
  34.             acks_cmd[]     = "ACKS",
  35. #ifdef POP_FOLDERS
  36.             fold_cmd[]     = "FOLD ",
  37. #endif
  38.             login_cmd[] = "HELO",
  39.             nack_cmd[]     = "NACK",
  40.             quit_cmd[]     = "QUIT",
  41.             read_cmd[]     = "READ",
  42.             retr_cmd[]     = "RETR";
  43.  
  44. /* Response messages */
  45. static char    count_rsp[] = "#%d messages in this folder\n",
  46.         error_rsp[]        = "- ERROR: %s\n",
  47.         greeting_msg[]     = "+ POP2 %s\n",
  48. /*        length_rsp[]       = "=%ld bytes in this message\n",         */
  49.         length_rsp[]       = "=%ld characters in msg #%d\n",
  50.         msg_line[]         = "%s\n",
  51.         no_mail_rsp[]      = "+ No mail, sorry\n",
  52.         no_more_rsp[]      = "=%d No more messages in this folder\n",
  53.         signoff_msg[]      = "+ Bye, thanks for calling\n";
  54.  
  55.  
  56. static void near
  57. state_error(struct popserv *scb,char *msg)
  58. {
  59.     usprintf(scb->socket,error_rsp,msg);
  60.     scb->state = DONE;
  61. }
  62.  
  63. static int near
  64. newmail(struct popserv *scb)
  65. {
  66.     struct stat folder_stat;
  67.  
  68.     if(stat(scb->path,&folder_stat) != 0) {
  69.         state_error(scb,"Unable to get old mail folder's status");
  70.         return(FALSE);
  71.     } else {
  72.         return((folder_stat.st_size > scb->folder_file_size) ? TRUE : FALSE);
  73.     }
  74. }
  75.  
  76. static int near
  77. isdeleted(struct popserv *scb,int msg_no)
  78. {
  79.     unsigned int offset = (--msg_no) / BITS_PER_WORD;
  80.     unsigned int mask = 1 << (msg_no % BITS_PER_WORD);
  81.  
  82.     return (((scb->msg_status[offset]) & mask) ? TRUE : FALSE);
  83. }
  84.  
  85. static void near
  86. close_folder(struct popserv *scb)
  87. {
  88.     char line[BUF_LEN];
  89.     FILE *fd;
  90.     int deleted = FALSE, msg_no = 0;
  91.     struct stat folder_stat;
  92.  
  93.     if(scb->wf == NULLFILE)
  94.         return;
  95.  
  96.     if(!scb->folder_modified) {
  97.         /* no need to re-write the folder if we have not modified it */
  98.         Fclose(scb->wf);
  99.         scb->wf = NULLFILE;
  100.  
  101.         xfree(scb->msg_status);
  102.         scb->msg_status = NULL;
  103.         return;
  104.     }
  105.     if(newmail(scb)) {
  106.         /* copy new mail into the work file
  107.          * and save the message count for later
  108.          */
  109.         if ((fd = Fopen(scb->path,READ_TEXT,0,1)) == NULLFILE)
  110.             return;
  111.  
  112.         fseek(scb->wf,0,SEEK_END);
  113.         fseek(fd,scb->folder_file_size,SEEK_SET);
  114.  
  115.         while(!feof(fd)) {
  116.             fgets(line,BUF_LEN,fd);
  117.             fputs(line,scb->wf);
  118.         }
  119.         Fclose(fd);
  120.     }
  121.     /* now create the updated mail folder */
  122.     if((fd = Fopen(scb->path,WRITE_TEXT,0,1)) == NULLFILE)
  123.         return;
  124.  
  125.     rewind(scb->wf);
  126.  
  127.     while(fgets(line,BUF_LEN,scb->wf) != NULL) {
  128.         if (isSOM(line)) {
  129.             msg_no++;
  130.             deleted = (msg_no <= scb->folder_len) ? isdeleted(scb,msg_no) : FALSE;
  131.         }
  132.         if(deleted)
  133.             continue;
  134.  
  135.         fputs(line,fd);
  136.     }
  137.     Fclose(fd);
  138.  
  139.     /* trash the updated mail folder if it is empty */
  140.     if((stat(scb->path,&folder_stat) == 0) && (folder_stat.st_size == 0))
  141.         unlink(scb->path);
  142.  
  143.     Fclose(scb->wf);
  144.     scb->wf = NULLFILE;
  145.     xfree(scb->msg_status);
  146.     scb->msg_status = NULL;
  147. }
  148.  
  149. static void near
  150. do_cleanup(struct popserv *scb)
  151. {
  152.     close_folder(scb);
  153.     scb->state = DONE;
  154. }
  155.  
  156. static void near
  157. deletemsg(struct popserv *scb,int msg_no)
  158. {
  159.     if(scb->def == FALSE) {
  160.         unsigned int offset = (--msg_no) / BITS_PER_WORD;
  161.         unsigned int mask = 1 << (msg_no % BITS_PER_WORD);
  162.  
  163.         scb->msg_status[offset] |= mask;
  164.         scb->folder_modified = TRUE;
  165.     }
  166. }
  167.  
  168. static void near
  169. print_message_length(struct popserv *scb)
  170. {
  171.     char *cp;
  172.  
  173.     if (scb->msg_len > 0 || scb->msg_num <= scb->folder_len) {
  174.         cp = length_rsp;
  175.     } else {
  176.         cp = no_more_rsp;
  177.     }
  178.     usprintf(scb->socket,cp,scb->msg_len,scb->msg_num);
  179. }
  180.  
  181. static void near
  182. get_message(struct popserv *scb,int msg_no)
  183. {
  184.     scb->msg_len = 0;
  185.  
  186.     if (msg_no > scb->folder_len) {
  187.         scb->curpos  = 0;
  188.         scb->nextpos = 0;
  189.         return;
  190.     } else {
  191.         char line[BUF_LEN];
  192.  
  193.         /* find the message and its length */
  194.         rewind(scb->wf);
  195.         while (!feof(scb->wf) && (msg_no > -1)) {
  196.             if (msg_no > 0)
  197.                 scb->curpos = ftell(scb->wf);
  198.  
  199.             fgets(line,BUF_LEN,scb->wf);
  200.             rip(line);
  201.  
  202.             if (isSOM(line))
  203.                 msg_no--;
  204.  
  205.             if (msg_no != 0)
  206.                 continue;
  207.  
  208.             scb->nextpos  = ftell(scb->wf);
  209.             scb->msg_len += (strlen(line)+2);    /* Add CRLF */
  210.         }
  211.     }
  212.  
  213.     if (scb->msg_len > 0)
  214.         fseek(scb->wf,scb->curpos,SEEK_SET);
  215.  
  216.     /* we need the pointers even if the message was deleted */
  217.     if(isdeleted(scb,scb->msg_num))
  218.         scb->msg_len = 0;
  219. }
  220.  
  221. static void near
  222. read_message(struct popserv    *scb)
  223. {
  224.     if(scb->buf[sizeof(read_cmd) - 1] == ' ') {
  225.         scb->msg_num = atoi(&(scb->buf[sizeof(read_cmd) - 1]));
  226.     } else {
  227.         scb->msg_num++;
  228.     }
  229.     get_message(scb,scb->msg_num);
  230.     print_message_length(scb);
  231.     scb->state  = ITEM;
  232. }
  233.  
  234. static void near
  235. retrieve_message(struct popserv    *scb)
  236. {
  237.     char line[BUF_LEN];
  238.     long cnt = scb->msg_len;
  239.  
  240.     if(cnt == 0) {
  241.         state_error(scb,"Msg already deleted");
  242.         return;
  243.     }
  244.     while(!feof(scb->wf) && (cnt > 0)) {
  245.         fgets(line,BUF_LEN,scb->wf);
  246.         rip(line);
  247.         usprintf(scb->socket,msg_line,line);
  248.         cnt -= (strlen(line) + 2);    /* Compensate for CRLF */
  249.     }
  250.     scb->state = NEXT;
  251. }
  252.  
  253. static void near
  254. open_folder(struct popserv *scb)
  255. {
  256.     char line[BUF_LEN];
  257.     FILE *fd;
  258.     struct stat folder_stat;
  259.  
  260.     scb->folder_len = 0;
  261.     scb->folder_file_size = 0;
  262.  
  263.     if(stat(scb->path,&folder_stat)){
  264.          usputs(scb->socket,no_mail_rsp);
  265.          return;
  266.     }
  267.     scb->folder_file_size = folder_stat.st_size;
  268.  
  269.     if((fd = Fopen(scb->path,READ_TEXT,0,1)) == NULLFILE)
  270.         return;
  271.  
  272.     if((scb->wf = Tmpfile(0,1)) == NULLFILE) {
  273.         Fclose(fd);
  274.         return;
  275.     }
  276.     while(fgets(line,BUF_LEN,fd) != NULL) {
  277.         /* scan for begining of a message */
  278.         if (isSOM(line))
  279.             scb->folder_len++;
  280.  
  281.         /* now put  the line in the work file */
  282.         fputs(line,scb->wf);
  283.     }
  284.     Fclose(fd);
  285.  
  286.     scb->msg_status_size = (scb->folder_len) / BITS_PER_WORD;
  287.  
  288.     if((((scb->folder_len) % BITS_PER_WORD) != 0) || (scb->msg_status_size == 0))
  289.         scb->msg_status_size++;
  290.  
  291.     scb->msg_status = cxallocw(scb->msg_status_size,sizeof(unsigned int));
  292.     usprintf(scb->socket,count_rsp,scb->folder_len);
  293.     scb->state  = MBOX;
  294. }
  295.  
  296. #ifdef POP_FOLDERS
  297. static void near
  298. select_folder(struct popserv *scb)
  299. {
  300.     char *cp = scb->buf;
  301.  
  302.     while(*cp++ != ' ') ;
  303.     strcpy(scb->username,cp);
  304.  
  305.     if (scb->wf != NULL)
  306.         close_folder(scb);
  307.     open_folder(scb);
  308. }
  309. #endif
  310.  
  311. void
  312. popserv(int s,void *unused,void *p)
  313. {
  314.     struct popserv *scb = mxallocw(sizeof(struct popserv));
  315.  
  316.     sockowner(s,Curproc);        /* We own it now */
  317.     sockmode(s,SOCK_ASCII);
  318.  
  319.     scb->folder_modified = FALSE;
  320.     scb->socket = s;
  321.     scb->state  = AUTH;
  322.  
  323.     usprintf(scb->socket,greeting_msg,Hostname);
  324.  
  325.     log(scb->socket,9983,"POP  open");
  326.  
  327.     for(;;) {
  328.         if (scb->state == DONE
  329.           || recvline(s,scb->buf,BUF_LEN) == -1) {
  330.             /* He closed on us */
  331.             break;
  332.         }
  333.         rip(scb->buf);
  334.         if (*scb->buf == '\0')        /* Ignore blank cmd lines */
  335.             continue;
  336.  
  337.         switch(scb->state) {
  338.         case AUTH: {
  339.             char tmp[5], cp[MAXPATH];
  340.  
  341.             sscanf(scb->buf,"%4s %s %s",tmp,scb->username,cp);
  342.  
  343.             if(strcmp(tmp,login_cmd) == 0) {
  344.                 if(!userlogin(IPPORT_POP,(void *)scb,cp)) {
  345. #ifdef ENHLOG
  346.                     log(scb->socket,IPPORT_POP,"POP  access DENIED to %s",scb->username);
  347. #endif
  348.                     state_error(scb,"Access denied");
  349.                     do_cleanup(scb);
  350.                     break;
  351.                 }
  352. #ifdef ENHLOG
  353.                 log(scb->socket,IPPORT_POP,"POP  access granted to %s",scb->username);
  354. #endif
  355.                 open_folder(scb);
  356.             } else if(strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
  357.                 do_cleanup(scb);
  358.             } else
  359.                 state_error(scb,"(AUTH) Expected HELO or QUIT command");
  360.             break;
  361.           }
  362.         case MBOX:
  363.             if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  364.                 read_message(scb);
  365. #ifdef POP_FOLDERS
  366.             else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  367.                 select_folder(scb);
  368. #endif
  369.             else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0) {
  370.                 do_cleanup(scb);
  371.             } else
  372.                 state_error(scb,
  373. #ifdef POP_FOLDERS
  374.                         "(MBOX) Expected FOLD, READ, or QUIT command");
  375. #else
  376.                         "(MBOX) Expected READ or QUIT command");
  377. #endif
  378.             break;
  379.         case ITEM:
  380.             if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  381.                 read_message(scb);
  382. #ifdef POP_FOLDERS
  383.             else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  384.                 select_folder(scb);
  385. #endif
  386.             else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
  387.                 retrieve_message(scb);
  388.             else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
  389.                 do_cleanup(scb);
  390.             else
  391.                 state_error(scb,
  392. #ifdef POP_FOLDERS
  393.                    "(ITEM) Expected FOLD, READ, RETR, or QUIT command");
  394. #else
  395.                    "(ITEM) Expected READ, RETR, or QUIT command");
  396. #endif
  397.             break;
  398.         case NEXT:
  399.             if (strncmp(scb->buf,ackd_cmd,strlen(ackd_cmd)) == 0){
  400.                 /* ACKD processing */
  401.                 deletemsg(scb,scb->msg_num);
  402.                 scb->msg_num++;
  403.                 get_message(scb,scb->msg_num);
  404.             } else if (strncmp(scb->buf,acks_cmd,strlen(acks_cmd)) == 0){
  405.                 /* ACKS processing */
  406.                 scb->msg_num++;
  407.                 get_message(scb,scb->msg_num);
  408.             } else if (strncmp(scb->buf,nack_cmd,strlen(nack_cmd)) == 0){
  409.                 /* NACK processing */
  410.                 fseek(scb->wf,scb->curpos,SEEK_SET);
  411.             } else {
  412.                 state_error(scb,"(NEXT) Expected ACKD, ACKS, or NACK command");
  413.                 break;
  414.             }
  415.             print_message_length(scb);
  416.             scb->state  = ITEM;
  417.             break;
  418.         case DONE:
  419.             break;
  420.         default:
  421.             state_error(scb,"(TOP) State Error!!");
  422.             break;
  423.         }
  424.     }
  425.     if(scb->state == DONE)
  426.         usputs(scb->socket,signoff_msg);
  427.  
  428.     do_cleanup(scb);
  429.  
  430.     log(scb->socket,9983,"POP  close");
  431.  
  432.     close_s(scb->socket);
  433.  
  434.     if(scb->wf != NULLFILE)
  435.         Fclose(scb->wf);
  436.     if(scb->msg_status  != NULL)
  437.         xfree(scb->msg_status);
  438.     if(scb->path != NULLCHAR)
  439.         xfree(scb->path);
  440.     xfree(scb);
  441. }
  442.  
  443. #endif /* POP */
  444.